#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
    Module Documentation
    here
"""

# Created by  : Zhang Chengdong
# Create Date : 2024/11/22 13:45
# Version = v0.1.0

__author__ = "Zhang Chengdong"
__copyright__ = "Copyright 2024. Large scale model"
__credits__ = ['Zhang Chengdong']

__liscence__ = "MIT"
__version__ = "1.0.1"
__maintainer__ = "Zhang Chengdong"
__status__ = "Production"

import os
from collections import defaultdict
import time
import json
import glob
import joblib
import numpy as np
import pandas as pd
# import seaborn as sns
from datetime import datetime
from typing import Tuple, Union
from collections import ChainMap
from catboost import CatBoostRegressor
from matplotlib import pyplot as plt
from sklearn.metrics import r2_score, mean_squared_error

from FeatureTool.feature_tools import FeatureTools as featool

def determine_model_use(data: dict):
    """
    判断数据应该使用哪一个模型   cement_real_strength_1d   cement_real_strength_3d
    :param data:
    :return:
    """
    model_type_suffix = {
        "model1": {"model_name": "model1", "use_name": "no_real_strength", "code": "CI_10001102"},
        "model3_1d": {"model_name": "model3_1d", "use_name": "with_real_strength_1d", "code": "CI_10001103"},
        "model3_n1d": {"model_name": "model3_n1d", "use_name": "no_real_strength", "code": "CI_10001103"},
        "model28_1d": {"model_name": "model28_1d", "use_name": "with_real_strength_1d", "code": "CI_10001104"},
        # 有一天，无3天 水泥实测值
        "model28_n1d": {"model_name": "model28_n1d", "use_name": "no_real_strength", "code": "CI_10001104"},
        # 无一天，无3天 水泥实测值
        "model28_3d": {"model_name": "model28_3d", "use_name": "with_real_strength_3d", "code": "CI_10001104"},
        # 有一天，有3天 水泥实测值
    }
    return model_type_suffix


def load_single_model(model_filename_path: str = None):
    """
    加载模型
    :param model_filename_path:
    :return:
    """
    model = joblib.load(model_filename_path)
    return model


def calculate_mixed_chemicals(ratio_key, data):
    """
    单个混合材的化学分析 计算细节
    :param ratio_key:
    :param data:
    :return:
    """
    ratio = data["搭配比例"][ratio_key]
    chemicals = data["materials_chemical_dict"]
    # 初始化混合物的化学成分
    mixed_chemicals = defaultdict(int)
    ratio = {key: int(value) if isinstance(value, str) else value for key, value in ratio.items()}
    total_ratio = sum(ratio.values())
    for material, weight in ratio.items():
        for chemical, value in chemicals[material].items():
            mixed_chemicals[chemical] += round((float(value) * float(weight)) / total_ratio, 3)
    return mixed_chemicals


def get_feed_back_ingredients(data: dict) -> list:
    """
    获取使用物料的列表
    :param data:
    """
    feed_back_ingredients = list(set(list(data['current_data']["dcs_feedback"].keys())))
    return feed_back_ingredients


def deal_mixed_chemicals(data: dict):
    """

    :param data:
    :return:
    """
    data['搭配比例'] = defaultdict()
    if data.get("gyp_ratio", None) is not None:
        data['搭配比例']["石膏"] = data["gyp_ratio"]
        del data['gyp_ratio']
    for item in data['admixture_ratio']:
        data['搭配比例'][item] = data['admixture_ratio'][item]

    del data['admixture_ratio']

    # for ratio_key in data["搭配比例"]:
    #     mixed_chemicals = calculate_mixed_chemicals(ratio_key, data)
    #     data["materials_chemical_dict"][ratio_key] = mixed_chemicals
    metric_list = get_feed_back_ingredients(data)
    if "石膏" in metric_list and "石膏" in data['搭配比例']:
        metric_list.remove("石膏")
        metric_list.extend(list(data['搭配比例']["石膏"].keys()))
    for item in metric_list:
        metric_name = item
        for ele_item in data['materials_chemical_dict'][item]:  # TODO 需要在这里加入相关提醒，物料的化学分析缺失问题
            new_metric = "{}_new_{}".format(metric_name, ele_item)
            data[new_metric] = data["materials_chemical_dict"][item][ele_item]
    del data['materials_chemical_dict']
    return data


def deal_process_data(data: dict):
    """
    处理过程质量
    :param data:
    :return:
    """
    for item in data['过程质量平均值']:
        data["{}-{}".format("过程质量平均值", item)] = data["过程质量平均值"][item]
    del data['过程质量平均值']
    return data


def deal_dcs_feedback(data: dict):
    """
    处理dcs反馈均值
    :param data:
    :return:
    """
    # TODO 如果需要化学分析则需要打开
    new_metric_dict = defaultdict()
    new_metric_list = list(data['搭配比例'].keys())
    for item in data['搭配比例']:
        item_count = float(data['DCS反馈配比平均值'][item])
        item_sum = sum(int(value) for value in data['搭配比例'][item].values())
        # 每个搭配比例搭配材料的数量
        for ele in data['搭配比例'][item]:
            if ele not in new_metric_dict:
                new_metric_dict[ele] = 0.
            if isinstance(data['搭配比例'][item][ele], str):
                data['搭配比例'][item][ele] = float(data['搭配比例'][item][ele])
            if data['搭配比例'][item][ele] == 0.:
                continue
            new_metric_dict[ele] += item_count * data['搭配比例'][item][ele] / item_sum
    for item in new_metric_list:
        del data['DCS反馈配比平均值'][item]
        if item not in new_metric_dict:
            continue
        data['DCS反馈配比平均值'][item] = new_metric_dict[item]
        del new_metric_dict[item]

    for item in new_metric_dict:
        if item not in data['DCS反馈配比平均值']:
            data['DCS反馈配比平均值'][item] = new_metric_dict[item]
        else:
            data['DCS反馈配比平均值'][item] += new_metric_dict[item]

    # 重命名key值部分
    for item in data['DCS反馈配比平均值']:
        data["{}-{}".format("DCS反馈配比平均值", item)] = data['DCS反馈配比平均值'][item]
    del data['DCS反馈配比平均值']
    return data


def characteristic_engineering(df: pd.DataFrame, feature_combination: list) -> pd.DataFrame:
    """
    使用对特征进行组合
    :param df:
    :param feature_combination
    """
    feature = featool(df, feature_combination)
    new_data_df = feature.combination_feature()
    return new_data_df


def predict_deal_data_for_model(data: dict, feature_columns: list, columns_mean: dict, feature_combination: list):
    """
    将请求参数处理成需要的
    :param data:
    :param feature_columns:
    :param columns_mean:
    :param feature_combination:
    :return:
    """
    # 熟料均化配置
    data['熟料3天强度预测'] = data['clinker_strength']['熟料3天强度预测']
    data['熟料28天强度预测'] = data['clinker_strength']['熟料28天强度预测']
    # TODO 需要和后端对接相关的参数名称
    data['工况1_1'] = abs(float(data['current_data']['difference_1d']))
    data['工况3_1'] = float(data['current_data']['difference_3_1d'])
    # del data['clinker_strength']
    # TODO 使用前两天的实测情况
    data['前2天1天实测均值'] = data['current_data']['mean_2d_strength']
    # 混合材和搭配比例计算化学分析
    data = deal_mixed_chemicals(data)

    # 处理过程质量
    data['过程质量平均值'] = data['current_data']['process_quality']
    data['DCS反馈配比平均值'] = data['current_data']['dcs_feedback']
    data = deal_process_data(data)

    # 处理DCS反馈配比平均值
    data = deal_dcs_feedback(data)

    # 处理1,3天实测
    data['水泥1天实测值'] = data['current_data'].get("cement_real_strength_1d", 0.0)
    data['水泥3天实测值'] = data['current_data'].get("cement_real_strength_3d", 0.0)
    # del data['current_data']['cement_real_strength_1d']
    # del data['current_data']['cement_real_strength_3d']
    # del data['current_data']

    # 磨号和水泥品种和时间
    data['时间'] = datetime.strptime(data['predict_time'], "%Y-%m-%d %H:%M:%S")
    month = data['时间'].month
    data['月份'] = "{}月".format(month)
    data['磨号'] = data['cement_mill']
    data['品种'] = data['cement_type']
    # del data['cement_type']
    # del data['cement_mill']
    # del data['predict_time']
    # 加入特征组合后的特征
    data_df = pd.DataFrame([data])
    data_df = characteristic_engineering(data_df, feature_combination)
    data = data_df.to_dict(orient='records')[0]
    for item in list(set(feature_columns) - set(list(data.keys()))):
        data[item] = np.NAN
        # if item not in columns_mean:
        #     data[item] = 0.0
        # else:
        #     data[item] = columns_mean[item]
    data = pd.DataFrame([data]).fillna(0)
    return data


def predict_single_item(data: pd.DataFrame, model: dict) -> dict:
    """
    对单条数据进行预测
    :param data:
    :param model:
    :return:
    """
    data.fillna(0, inplace=True)
    feature_columns_list = model['feature_names']
    y_pred = model['model'].predict(data[feature_columns_list])
    model_name = model['model_name']
    info = {
        "model_name": model_name,
        "predict": y_pred
    }
    return info


def predict_main(data: dict, model_infos: dict):
    """
    预测数据的主入口
    :param data:
    :param model_info:
    :return:
    """
    data_copy = data.copy()
    model_path = model_infos['model_path']
    if not os.path.exists(model_path):
        return {model_infos['first_suffix']: {"error_msg": "{}预测文件不存在！".format(model_infos['first_suffix'])}}
    model_info = load_single_model(model_path)
    model = model_info['model']
    feature_columns = model_info['feature_names']
    columns_mean = model_info['columns_mean']
    feature_combination = model_info['feature_combination']
    use_pca_func = model_info['use_pca_func']
    linear_coef = model_info['linear_coef']

    # mean_1d = model_info['mean_1d']
    # mean_3d = model_info['mean_3d']
    mean_2d = float(data_copy['current_data'].get('mean_2d_strength') or 0.0)
    real_strength_3d = float(data_copy['current_data'].get('cement_real_strength_3d') or 0.0)
    if mean_2d == 0.:
        data_copy['current_data']['mean_2d_strength'] = columns_mean['水泥1天实测值']
    if real_strength_3d == 0.:
        data_copy['current_data']['cement_real_strength_3d'] = columns_mean['水泥3天实测值']

    data_df = predict_deal_data_for_model(data_copy, feature_columns, columns_mean, feature_combination)
    if use_pca_func:
        pca_path = os.path.join(settings.save_model_path, "PCAmodel", 'PCA.pkl')
        trans_pca_model_info = load_single_model(pca_path)
        pac_columns = trans_pca_model_info['feature_list']
        pac_model = trans_pca_model_info['pca_model']
        x_pca_feature = pac_model.transform(data_df[pac_columns])
        x_pca_df = pd.DataFrame(x_pca_feature, columns=['pca_feature'])
        data_df = pd.concat([data_df, x_pca_df], axis=1)
    y_pred = model.predict(data_df.loc[:, feature_columns])
    return {model_infos['first_suffix']: {model_infos['second_suffix']: str(round(y_pred[0], 1)), "error_msg": "",
                                          "{}_linear_coef".format(model_infos['model_type']): linear_coef}}
